package top.likeqc.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import top.likeqc.common.lang.Const;
import top.likeqc.entity.SysMenu;
import top.likeqc.entity.SysRole;
import top.likeqc.entity.SysUser;
import top.likeqc.mapper.SysUserMapper;
import top.likeqc.service.SysMenuService;
import top.likeqc.service.SysRoleService;
import top.likeqc.service.SysUserService;
import top.likeqc.util.RedisUtil;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author likeqc
 * @since 2022-03-14
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
        implements SysUserService {

    @Autowired SysRoleService sysRoleService;

    @Autowired SysUserMapper sysUserMapper;

    @Autowired SysMenuService sysMenuService;

    @Autowired RedisUtil redisUtil;

    @Autowired
    BCryptPasswordEncoder passwordEncoder;

    @Override
    public SysUser getByUsername(String username) {

        return getOne(new QueryWrapper<SysUser>().eq("username", username));
    }

    @Override
    public String getUserAuthorityInfo(Long userId) {
        // ROLE_admin,ROLE_normal,sys:user:list,...
        String authority = "";

        if (redisUtil.hasKey("GrantedAuthority:" + userId)) {
            authority = (String) redisUtil.get("GrantedAuthority:" + userId);
        } else {
            // 获取角色
            // TODO 是否有 SQL 注入风险
            List<SysRole> roles =
                    sysRoleService.list(
                            new QueryWrapper<SysRole>()
                                    .inSql(
                                            "id",
                                            "select role_id from sys_user_role where user_id "
                                                    + "= "
                                                    + userId));

            if (roles.size() > 0) {
                String roleCodes =
                        roles.stream()
                                .map(r -> "ROLE_" + r.getCode())
                                .collect(Collectors.joining(","));
                authority = roleCodes.concat(",");
            }
            // 获取菜单操作编码
            List<Long> menuIds = sysUserMapper.getNavMenuId(userId);
            if (menuIds.size() > 0) {
                List<SysMenu> menus = sysMenuService.listByIds(menuIds);
                String menuPerms =
                        menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));

                authority = authority.concat(menuPerms);
            }
            // 结果加到缓存
            // TODO 什么情况下需要及时删除缓存
            redisUtil.set("GrantedAuthority:" + userId, authority, 60 * 60);
        }

        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(Long userId) {
        redisUtil.del("GrantedAuthority:" + userId);
    }

    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {
        List<SysUser> sysUsers = this.list(new QueryWrapper<SysUser>()
                .inSql("id", "select user_id from sys_user_role where role_id = " + roleId));

        sysUsers.forEach(u -> {
            this.clearUserAuthorityInfo(u.getId());
        });
    }

    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
        // TODO 有待优化，直接查询出 userIds，减少不必要的连表
        List<SysUser> sysUsers = sysUserMapper.listByMenuId(menuId);

        sysUsers.forEach(u -> {
            if (u != null) {
                this.clearUserAuthorityInfo(u.getId());
            }
        });
    }

    @Override
    public void addUser(SysUser sysUser) {
        sysUser.setUpdated(LocalDateTime.now());
        sysUser.setPassword(passwordEncoder.encode(Const.DEFULT_PASSWORD));
        sysUser.setAvatar(Const.DEFULT_AVATAR);

        save(sysUser);
    }
}
